package com.fdh.myjdbcutil;

import java.sql.ResultSet;
import java.sql.Statement;
import java.util.*;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

/**
 * 自动化的ResultSet管理
 *
 * @author fdh 2024.8.12 14:49
 * @version 1.0
 */
public class ResultSetManager {
    private static Config config = new Config();
    private static HashMap<ResultSet, ResultSetGroup> resultSetGroupMap = new HashMap<>(config.listStorageDefaultSize);
    private static int currentResultSetCount = 0;
    private static final Lock lock = new ReentrantLock();

    /**
     * 配置类
     */
    public static class Config {
        /**
         * 一个ResultSet的最大存储时间,毫秒单位
         *
         * <p>默认是5s</p>
         */
        public long maximumIdleTime = 5 * 1000L;

        /**
         * 每增加n个ResultSet更新一次ResultSetGroup(自动管理ResultSet的列表结构)去除超时ResultSet
         *
         * <p>默认15个</p>
         */
        public int numberOfUpdateIntervals = 15;

        /**
         * ResultSet列表结构存储的默认大小,如果超出会自动扩容
         *
         * <p>默认100</p>
         */
        public int listStorageDefaultSize = 100;

        /**
         * 是否刷新ResultSet的更新时间,相同的ResultSet被再次使用时会刷新时间
         *
         * <p>默认false</p>
         */
        public boolean isRefreshResultsSetUpdateTime = false;
    }

    /**
     * 设置配置类,线程安全,但不推荐多次更改
     *
     * @param config 配置类
     */
    public static void setConfig(Config config) {
        try {
            lock.lock();
            ResultSetManager.config = config;
            resultSetGroupMap = new HashMap<>(config.listStorageDefaultSize);
        } catch (Exception e) {
            if (MyJdbcUtil.getInstance().getIsUseReminderMessage())
                System.out.println("提醒:ResultSetManager配置出错:" + e.getMessage());
            if (MyJdbcUtil.getInstance().getIsUseErrorStack())
                e.printStackTrace();
        } finally {
            lock.unlock();
        }
    }

    /**
     * 清空ResultSetGroups(自动管理ResultSet的列表结构),线程安全
     */
    public static void clearResultSetGroups() {
        try {
            lock.lock();
            resultSetGroupMap.clear();
            currentResultSetCount = 0;
        } catch (Exception e) {
            if (MyJdbcUtil.getInstance().getIsUseReminderMessage())
                System.out.println("提醒:ResultSetManager清空ResultSetGroups出错:" + e.getMessage());
            if (MyJdbcUtil.getInstance().getIsUseErrorStack())
                e.printStackTrace();
        } finally {
            lock.unlock();
        }
    }

    /**
     * 关闭全部ResultSet,线程安全
     */
    public static void closeAllResultSet() {
        try {
            lock.lock();
            resultSetGroupMap.values().forEach(ResultSetGroup::close);
            clearResultSetGroups();
        } catch (Exception e) {
            if (MyJdbcUtil.getInstance().getIsUseReminderMessage())
                System.out.println("提醒:ResultSetManager关闭全部ResultSetGroups出错:" + e.getMessage());
            if (MyJdbcUtil.getInstance().getIsUseErrorStack())
                e.printStackTrace();
        } finally {
            lock.unlock();
        }
    }

    /**
     * 添加新的ResultSet,线程安全
     * <p>
     * 注意ResultSet结束后,Statement和PreparedStatement也同时关闭
     *
     * @param resultSet 托管的ResultSet
     * @param statement 托管的Statement
     */
    public static void addNewResultSet(ResultSet resultSet, Statement statement) {
        try {
            lock.lock();
            if (closeResultSet(resultSet, false)) {
                return;
            }
            if (resultSetGroupMap.containsKey(resultSet)) {
                if (config.isRefreshResultsSetUpdateTime) {
                    resultSetGroupMap.get(resultSet).updateTime();
                }
            } else {
                ResultSetGroup group = new ResultSetGroup(resultSet, statement);
                resultSetGroupMap.put(resultSet, group);
                currentResultSetCount++;
            }
            if (currentResultSetCount >= config.numberOfUpdateIntervals) {
                updateResultSetGroups();
                currentResultSetCount = 0;
            }
        } catch (Exception e) {
            if (MyJdbcUtil.getInstance().getIsUseReminderMessage())
                System.out.println("提醒:ResultSetManager添加ResultSetGroup出错:" + e.getMessage());
            if (MyJdbcUtil.getInstance().getIsUseErrorStack())
                e.printStackTrace();
        } finally {
            lock.unlock();
        }
    }

    /**
     * 获取resultSetGroups存储列表的大小
     *
     * @return 大小
     */
    public static int resultSetGroupsSize() {
        try {
            lock.lock();
            return resultSetGroupMap.size();
        } catch (Exception e) {
            if (MyJdbcUtil.getInstance().getIsUseReminderMessage())
                System.out.println("提醒:ResultSetManager获取resultSetGroups大小出错:" + e.getMessage());
            if (MyJdbcUtil.getInstance().getIsUseErrorStack())
                e.printStackTrace();
        } finally {
            lock.unlock();
        }
        return -1;
    }

    /**
     * 结果集组合
     */
    private static class ResultSetGroup {
        public ResultSet resultSet;
        public Statement statement;
        public long updateTime;

        public ResultSetGroup(ResultSet resultSet, Statement statement) {
            this.resultSet = resultSet;
            this.statement = statement;
            this.updateTime = System.currentTimeMillis();
        }

        public void updateTime() {
            this.updateTime = System.currentTimeMillis();
        }

        public void close() {
            try {
                if (resultSet != null && !resultSet.isClosed()) {
                    resultSet.close();
                }
                if (statement != null && !statement.isClosed()) {
                    statement.close();
                }
            } catch (Exception e) {
                if (MyJdbcUtil.getInstance().getIsUseReminderMessage())
                    System.out.println("提醒:ResultSetManager关闭ResultSet,Statement或者PreparedStatement出错:" + e.getMessage());
                if (MyJdbcUtil.getInstance().getIsUseErrorStack())
                    e.printStackTrace();
            }
        }
    }

    /**
     * 更新ResultSetGroups
     */
    private static void updateResultSetGroups() {
        Set<ResultSet> resultSets = resultSetGroupMap.keySet();
        Iterator<ResultSet> iterator = resultSets.iterator();
        while (iterator.hasNext()) {
            ResultSet next = iterator.next();
            if (closeResultSet(next, false)) {
                iterator.remove();
                continue;
            }
            ResultSetGroup resultSetGroup = resultSetGroupMap.get(next);
            long time = System.currentTimeMillis() - resultSetGroup.updateTime;
            if (time >= config.maximumIdleTime) {
                closeResultSet(resultSetGroup.resultSet, true);
                iterator.remove();
            }
        }
    }

    /**
     * 关闭ResultSet
     *
     * @param resultSet    结果集
     * @param isForceClose 是否强制关闭
     * @return 是否关闭
     */
    private static boolean closeResultSet(ResultSet resultSet, boolean isForceClose) {
        try {
            if (resultSet == null || resultSet.isClosed()) {
                ResultSetGroup resultSetGroup = resultSetGroupMap.get(resultSet);
                if (resultSetGroup != null) {
                    resultSetGroup.close();
                }
                return true;
            }
            if (isForceClose || resultSet.isLast() || resultSet.isAfterLast()) {
                ResultSetGroup resultSetGroup = resultSetGroupMap.get(resultSet);
                if (resultSetGroup == null) {
                    resultSet.close();
                } else {
                    resultSetGroup.close();
                }
                return true;
            }
        } catch (Exception e) {
            if (MyJdbcUtil.getInstance().getIsUseReminderMessage())
                System.out.println("提醒:ResultSetManager关闭ResultSet出错:" + e.getMessage());
            if (MyJdbcUtil.getInstance().getIsUseErrorStack())
                e.printStackTrace();
        }
        return false;
    }
}
